import json
import requests
import urllib.parse as urlparse
import time
import hashlib
import hmac
import sys
import string
import random

from coin.exchange.base.kr_rest.auth_base import AuthKey
from coin2.api_client.common import (SyncQueryer)
from enum import Enum

class Type(Enum):
  BUY = "buy"
  SELL = "sell"
  BUY_MARKET = "buy_market"
  SELL_MARKET = "sell_market"
  BUY_MAKER = "buy_maker"
  SELL_MAKER = "sell_maker"
  BUY_IOC = "buy_ioc"
  SELL_IOC = "sell_ioc"
  BUY_FOK = "buy_fok"
  SELL_FOK = "sell_fok"

class LbankAuth(requests.auth.AuthBase):
  def __init__(self, key):
    self._key = AuthKey.from_file(key)

  def _gen_signature(self, body: dict):
    sorted_dict = {k: body[k] for k in sorted(body)}
    parameters = urlparse.urlencode(sorted_dict)

    prepared_str = hashlib.md5(parameters.encode()).hexdigest().upper().encode()

    return hmac.new(self._key.secret_key_bytes, prepared_str, digestmod=hashlib.sha256).hexdigest()

  def __call__(self, req: requests.PreparedRequest):
    timestamp = str(int(time.time() * 1000))
    signature_method = "HmacSHA256"

    num = string.ascii_letters + string.digits
    echostr = "".join(random.sample(num, 35))

    body = dict(urlparse.parse_qsl(req.body))
    body['api_key'] = self._key.access_key
    body['timestamp'] = timestamp
    body['signature_method'] = signature_method
    body['echostr'] = echostr

    sign = self._gen_signature(body)
    body['sign'] = sign

    req.prepare_body(body, None)

    # print(req.headers)
    # print(req.body)
    # print(req.url)

    return req

class LbankPrivateClient():
  def __init__(self, key_path: str, ip=None):
    self._auth = LbankAuth(key_path)
    self._url = "https://api.lbank.info"

    self._query = SyncQueryer(api_host=self._url, auth=self._auth, ip=ip)

  def get_account_balance(self):
    method = "POST"
    path = "/v2/supplement/user_info.do"

    response = self._query.query(method=method, path=path)
    return response.json()

  def get_fills(self, symbol: str):
    assert symbol, "symbol must be not None and not empty!"

    data = {
      "symbol": symbol,
    }
    method = "POST"
    path = "/v2/supplement/transaction_history.do"

    response = self._query.query(method=method, path=path, data=data)
    return response.json()

  def get_open_order(self, symbol: str):
    assert symbol, "symbol must be not None and not empty!"

    method = "POST"
    data = {
      'symbol': symbol,
      'current_page': 1,
      'page_length': 200,
    }
    path = "/v2/supplement/orders_info_no_deal.do"

    response = self._query.query(method=method, path=path, data=data)
    return response.json()

  def cancel_order(
    self,
    symbol: str,
    order_id: str = None,
    orig_client_order_id: str = None
  ):
    assert symbol, "symbol must be not None and not empty!"
    one_and_only_one = (order_id == None and orig_client_order_id != None) or (order_id != None and orig_client_order_id == None)
    assert one_and_only_one, "orderId and origClientOrderId have one and only one!"

    method = "POST"
    data = {
      'symbol': symbol,
      'orderId': order_id,
      'origClientOrderId': orig_client_order_id,
    }
    path = "/v2/supplement/cancel_order.do"

    response = self._query.query(method=method, path=path, data=data)
    return response.json()

  def submit_order(
    self,
    symbol: str,
    type: Type,
    amount: str,
    price: str = None,
    custom_id: str = None
  ):
    assert symbol, "symbol must be not None and not empty!"
    assert type, "type must be not None!"
    assert amount, "amount must be not None and not empty!"

    method = "POST"
    data = {
      'symbol': symbol,
      'type': type.value,
      'amount': amount,
      'price': price,
      'custom_id': custom_id,
    }
    path = "/v2/supplement/create_order.do"

    response = self._query.query(method=method, path=path, data=data)
    return response.json()

# api doc url
# https://www.lbank.com/en-US/docs/index.html#introduction

if __name__ == "__main__":
  key_path = sys.argv[1]
  
  lbank = LbankPrivateClient(key_path)
  # info = lbank.get_account_balance()
  # info = lbank.get_fills("btc_usdt")
  info = lbank.get_open_order("btc_usdt")
  # info = lbank.cancel_order("btc_usdt", orig_client_order_id="test_id")
  # info = lbank.submit_order("btc_usdt", Type.BUY, "1", "1", "test_id")
  print(json.dumps(info, indent=2))
